# StalactiteRepositoryConfigExtension.java

    package org.codefilarete.stalactite.spring.repository.config;

    import java.util.Collection;
    import java.util.Collections;
    import java.util.LinkedHashSet;
    import java.util.Locale;
    import java.util.Optional;
    import java.util.Set;

    import org.codefilarete.stalactite.spring.repository.StalactiteRepository;
    import org.codefilarete.stalactite.spring.repository.StalactiteRepositoryFactoryBean;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
    import org.springframework.data.repository.config.DefaultRepositoryConfiguration;
    import org.springframework.data.repository.config.RepositoryConfiguration;
    import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
    import org.springframework.data.repository.config.RepositoryConfigurationSource;
    import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
    import org.springframework.lang.Nullable;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.StringUtils;

    /**
     * Stalactite specific configuration extension parsing custom attributes from the XML namespace and
     * {@link EnableStalactiteRepositories} annotation.
     * Implementation is inspired by {@link org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension}
     * which was taken as an example.
     *
     * @author Guillaume Mary
     */
    public class StalactiteRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport {

    	private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
    	private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";

    	@Override
    	public String getModuleName() {
    		return "Stalactite";
    	}

    	@Override
    	public String getRepositoryFactoryBeanClassName() {
    		return StalactiteRepositoryFactoryBean.class.getName();
    	}

    	@Override
    	protected String getModulePrefix() {
    		return getModuleName().toLowerCase(Locale.US);
    	}

    	/* No annotations with Stalactite, so no reason to scan for entities
    	@Override
    	protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
    		return Arrays.asList(Entity.class, MappedSuperclass.class);
    	}
    	*/

    	@Override
    	protected Collection<Class<?>> getIdentifyingTypes() {
    		return Collections.singleton(StalactiteRepository.class);
    	}

    	@Override
    	public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
    		Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
    		builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
    	}

    	@Override
    	public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {

    		AnnotationAttributes attributes = config.getAttributes();

    		builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE,
    				attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE));
    	}

    	@Override
    	public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) {

    		Optional<String> enableDefaultTransactions = config.getAttribute(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE);

    		if (enableDefaultTransactions.isPresent() && StringUtils.hasText(enableDefaultTransactions.get())) {
    			builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE, enableDefaultTransactions.get());
    		}
    	}

    	@Override
    	protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) {

    		ClassLoader classLoader = loader.getClassLoader();

    		return classLoader != null && LazyJvmAgent.isActive(loader.getClassLoader())
    				? new InspectionClassLoader(loader.getClassLoader())
    				: loader.getClassLoader();
    	}

    	/**
    	 * Overridden to adapt the configSource argument to Stalactite case and ovoid exception throwing : some expected
    	 * options are not available in {@link EnableStalactiteRepositories} but {@link AnnotationRepositoryConfigurationSource}
    	 * doesn't handle their missing correctly and throws an exception.
    	 *
    	 * @param definition the definition of the bean-repository being created
    	 * @param configSource a {@link AnnotationRepositoryConfigurationSource} that gives access to the real {@link EnableStalactiteRepositories} info
    	 * @return a {@link DefaultRepositoryConfiguration}
    	 * @param <T> type of configuration source, for us a {@link AnnotationRepositoryConfigurationSource}
    	 */
    	@Override
    	protected <T extends RepositoryConfigurationSource> RepositoryConfiguration<T> getRepositoryConfiguration(
    			BeanDefinition definition, T configSource) {
    		// Note that we can cast given argument to AnnotationRepositoryConfigurationSource because that's what is
    		// instantiated in RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions(..) which is the code
    		// that invokes us with different intermediaries.
    		T repositoryConfigurationSource = (T) new EnableStalactiteRepositoriesRepositoryConfigurationSource((AnnotationRepositoryConfigurationSource) configSource);
    		return new DefaultRepositoryConfiguration<>(repositoryConfigurationSource, definition, this);
    	}

    	/**
    	 * Utility to determine if a lazy Java agent is being used that might transform classes at a later time.
    	 */
    	static class LazyJvmAgent {

    		private static final Set<String> AGENT_CLASSES;

    		static {

    			Set<String> agentClasses = new LinkedHashSet<>();

    			agentClasses.add("org.springframework.instrument.InstrumentationSavingAgent");
    			agentClasses.add("org.eclipse.persistence.internal.jpa.deployment.JavaSECMPInitializerAgent");

    			AGENT_CLASSES = Collections.unmodifiableSet(agentClasses);
    		}

    		private LazyJvmAgent() {}

    		/**
    		 * Determine if any agent is active.
    		 *
    		 * @return {@literal true} if an agent is active.
    		 */
    		static boolean isActive(@Nullable ClassLoader classLoader) {

    			return AGENT_CLASSES.stream() //
    					.anyMatch(agentClass -> ClassUtils.isPresent(agentClass, classLoader));
    		}
    	}
    }